<?php
namespace App\Http\Controllers\Business;

class MatlabTool
{
    // A Library of Matrix Math functions.
    // All assume a Matrix defined by a 2 dimensional array, where the first
    //  index (array[x]) are the rows and the second index (array[x][y])
    //  are the columns
    // First create a few helper functions
    // A function to determine if a matrix is well formed.  That is to say that
    //  it is perfectly rectangular with no missing values:
    function _matrix_well_formed($matrix) {
        // If this is not an array, it is badly formed, return false.
        if (!(is_array($matrix))) {
            return false;
        } else {
            // Count the number of rows.
            $rows = count($matrix);
            $cols = 0;
            // Now loop through each row:
            for ($r = 0; $r < $rows; $r++) {
                // Make sure that this row is set, and an array.  Checking to
                //  see if it is set is ensuring that this is a 0 based
                //  numerically indexed array.
                if (!(isset($matrix[$r]) && is_array($matrix[$r]))) {
                    return false;
                } else {
                    // If this is row 0, calculate the columns in it:
                    if ($r == 0) {
                        $cols = count($matrix[$r]);
                        // Ensure that the number of columns is identical else exit
                    } elseif (count($matrix[$r]) != $cols) {
                        return false;
                    }
                    // Now, loop through all the columns for this row
                    for ($c = 0; $c < $cols; $c++) {
                        // Ensure this entry is set, and a number
                        if (!(isset($matrix[$r][$c]) &&
                            is_numeric($matrix[$r][$c]))) {
                            return false;
                        }
                    }
                }
            }
        }

        // Ok, if we actually made it this far, then we have not found
        //  anything wrong with the matrix.
        return true;
    }
    // A function to return the rows in a matrix -
    //   Does not check for validity, it assumes the matrix is well formed.
    function _matrix_rows($matrix) {
        return count($matrix);
    }
    // A function to return the columns in a matrix -
    //   Does not check for validity, it assumes the matrix is well formed.
    function _matrix_columns($matrix) {
        return count($matrix[0]);
    }
    // This function performs operations on matrix elements, such as addition
    //  or subtraction. To use it, pass it 2 matrices, and the operation you
    //  wish to perform, as a string: '+', '-'
    function matrix_element_operation($a, $b, $operation) {
        // Verify both matrices are well formed
        $valid = false;
        if (_matrix_well_formed($a) && _matrix_well_formed($b)) {
            // Make sure they have the same number of columns & rows
            $rows = _matrix_rows($a);
            $columns = _matrix_columns($a);

            if (($rows == _matrix_rows($b)) &&
                ($columns == _matrix_columns($b))) {
                // We have a valid setup for continuing with element math
                $valid = true;
            }
        }

        // If invalid, return false
        if (!($valid)) {
            return false;
        }

        // For each element in the matrices perform the operatoin on the
        //  corresponding element in the other array to it:
        for ($r = 0; $r < $rows; $r++) {
            for ($c = 0; $c < $columns; $c++) {
                eval('$a[$r][$c] ' . $operation . '= $b[$r][$c];');
            }
        }

        // Return the finished matrix:
        return $a;
    }
    // This function performs full matrix operations, such as matrix addition
    //  or matrix multiplication.  As above, pass it to matrices and the
    //  operation: '*', '-', '+'
    function matrix_operation($a, $b, $operation) {
        // Verify both matrices are well formed
        $valid = false;
        if ($this->_matrix_well_formed($a) && $this->_matrix_well_formed($b)) {
            // Make sure they have complementary numbers of rows and columns.
            // The number of rows in A should be the number of columns in B
            $rows = $this->_matrix_rows($a);
            $columns = $this->_matrix_columns($a);

            if (($columns == $this->_matrix_rows($b)) &&
                ($rows == $this->_matrix_columns($b))) {
                // We have a valid setup for continuing
                $valid = true;
            }
        }

        // If invalid, return false
        if (!($valid)) {
            return false;
        }

        // Create a blank matrix the appropriate size, initialized to 0
        $new = array_fill(0, $rows, array_fill(0, $rows, 0));

        // For each row in a ...
        for ($r = 0; $r < $rows; $r++) {
            // For each column in b ...
            for ($c = 0; $c < $rows; $c++) {
                // Take each member of column b, with each member of row a
                // and add the results, storing this in the new table:
                // Loop over each column in A ...
                for ($ac = 0; $ac < $columns; $ac++) {
                    // Evaluate the operation
                    eval('$new[$r][$c] += $a[$r][$ac] ' .
                        $operation . ' $b[$ac][$c];');
                }
            }
        }
        // Return the finished matrix:
        return $new;
    }
    // A function to perform scalar operations.  This means that you take the scalar value,
    //  and the operation provided, and apply it to every element.
    function matrix_scalar_operation($matrix, $scalar, $operation)
    {
        // Verify it is well formed
        if ($this->_matrix_well_formed($matrix)) {
            $rows = $this->_matrix_rows($matrix);
            $columns = $this->_matrix_columns($matrix);
            // For each element in the matrix, multiply by the scalar
            for ($r = 0; $r < $rows; $r++) {
                for ($c = 0; $c < $columns; $c++) {
                    eval('$matrix[$r][$c] ' . $operation . '= $scalar;');
                }
            }

            // Return the finished matrix:
            return $matrix;
        } else {
            // It wasn't well formed:
            return false;
        }
    }

// A handy function for printing matrices (As an HTML table)
    function matrix_print($matrix)
    {
        // Verify it is well formed
        if (_matrix_well_formed($matrix)) {
            $rows = _matrix_rows($matrix);
            $columns = _matrix_columns($matrix);
            // Start the table
            echo '<table>';

            // For each row in the matrix:
            for ($r = 0; $r < $rows; $r++) {
                // Begin the row:
                echo '<tr>';
                // For each column in this row
                for ($c = 0; $c < $columns; $c++) {
                    // Echo the element:
                    echo "<td>{$matrix[$r][$c]}</td>";
                }
                // End the row.
                echo '</tr>';
            }
            // End the table.
            echo "</table>/n";
        } else {
            // It wasn't well formed:
            return false;
        }
    }
    //逆矩阵
    function getNiMatrix($matrix) {
        /*定义扩展矩阵*/
        for ($i = 0; $i < count($matrix); $i++) {
            for ($j = 0; $j < count($matrix) * 2; $j++) {
                $expandMatrix[$i][$j] = 0;
            }
        }
        for ($i = 0; $i < count($matrix); $i++) {
            for ($j = 0; $j < count($matrix); $j++) {
                /*定义得到的逆矩阵*/
                $newMatrix[$i][$j] = 0;
            }
        }
        /*初始化扩展矩阵*/
        $expandMatrix = $this->initExpandMatrix($matrix, $expandMatrix);
        /*调整扩展矩阵，若某一列全为0，则行列式的值等于0，不存在逆矩阵*/
        $canAdjust = $this->adjustMatrix($expandMatrix);
        if (false == $canAdjust)//如果不存在逆矩阵，返回NULL
            return null;
        /*计算扩展矩阵*/
        $expandMatrix = $this->calculateExpandMatrix($expandMatrix);
        /*用计算过的扩展矩阵取后面的N*N矩阵，为所求*/
        $newMatrix = $this->getNewMatrix($expandMatrix, $newMatrix);
        return $newMatrix;
    }

    /*初始化扩展矩阵*/
    function initExpandMatrix($initMatrix, $expandMatrix) {
        for ($i=0; $i<count($initMatrix)* 2; $i++) {
            for ($j=0; $j < count($initMatrix[$i])* 2; $j++) {
                if ($j < count($initMatrix)) {//左边的N*N矩阵原样赋值
                    if ($i >= count($initMatrix)) {
                        $expandMatrix[$i][$j] = 0;
                    } else {
                        $expandMatrix[$i][$j] = $initMatrix[$i][$j];
                    }
                } else {    //右边N*N赋值为单位矩阵
                    $expandMatrix[$i][$j] = (($i + count($initMatrix)) == $j) ? 1 : 0;
                }
            }
        }
        return $expandMatrix;
    }

    /*调整扩展矩阵，若某一列全为0，则行列式的值等于0，不存在逆矩阵*/
    function adjustMatrix($expandMatrix)
    {
        for ($i = 0; $i < count($expandMatrix); $i++) {
            if ($expandMatrix[$i][$i] == 0) {//如果某行对角线数值为0
                /*搜索该列其他不为0的行，如果都为0，则返回false*/
                for ($j = 0; $j < count($expandMatrix); $j++) {
                    if ($expandMatrix[$j][$i] != 0) {//如果有不为0的行，交换这两行
                        $temp = $expandMatrix[$i];
                        $expandMatrix[$i] = $expandMatrix[$j];
                        $expandMatrix[$j] = $temp;
                        break;
                    }
                }
                if ($j >= count($expandMatrix)) {//没有不为0的行
                    return false;
                }
            }
        }
        return true;
    }

    /*计算扩展矩阵*/
    function calculateExpandMatrix($expandMatrix)
    {
        for ($i = 0; $i < count($expandMatrix); $i++) {
            $firstElement = $expandMatrix[$i][$i];
            for ($j = 0; $j < count($expandMatrix[$i]); $j++) {
                $expandMatrix[$i][$j] /= $firstElement;//将该行所有元素除以首元素
            }
            /*把其他行再该列的数值都化为0*/
            for ($m = 0; $m < count($expandMatrix); $m++) {
                if ($m == $i) {//遇到自己的行跳过
                    continue;
                }
                $beishu = $expandMatrix[$m][$i];
                for ($n = 0; $n < count($expandMatrix[$i]); $n++) {
                    $expandMatrix[$m][$n] -= $expandMatrix[$i][$n] * $beishu;
                }
            }
        }
        return $expandMatrix;
    }

    /*用计算过的扩展矩阵取后面的N*N矩阵，为所求*/
    function getNewMatrix($expandMatrix, $newMatrix)
    {
        for ($i = 0; $i < count($expandMatrix); $i++) {
            for ($j = 0; $j < count($expandMatrix[$i]); $j++) {
                if ($j >= count($expandMatrix)) {
                    $newMatrix[$i][$j - count($expandMatrix)] = $expandMatrix[$i][$j];
                }
            }
        }
        return $newMatrix;
    }

    /*打印矩阵*/
    function printMatrix($matrix)
    {

    }

    /*矩阵做乘法，验证结果*/
    function getProductMatrix($initMatrix, $newMatrix)
    {
        $len = count($initMatrix);
        $productMatrix = [];
        for ($i = 0; $i < $len; $i++) {
            for ($j = 0; $j < $len; $j++) {
                for ($k = 0; $k < $len; $k++) {
                    $productMatrix[$i][$j] += $initMatrix[$i][$k] * $newMatrix[$k][$j];
                }
            }
        }
        return $productMatrix;
    }

    //$initMatrix = [[1, 2, -1 ], [3, 4, -2 ], [5, -4, 1 ]];
//    $initMatrix =[[1,1,-1],[0,1,1],[0,0,1]];
    //$initMatrix = [[1, 3, 4, 3],[0, 2, 3, 0],[1, 1, 1, 3],[0, 0, 3, 0]];
//    $returnData = getNiMatrix($initMatrix);
//    var_dump($returnData);

// Now let's test element operations.  We need identical sized matrices:
//$m1 = array(
//    array(5, 3, 2),
//    array(3, 0, 4),
//    array(1, 5, 2),
//);
//$m2 = array(
//    array(4, 9, 5),
//    array(7, 5, 0),
//    array(2, 2, 8),
//);

// Element addition should give us:  9    12     7
//                                  10     5     4
//                                   3     7    10
//matrix_print(matrix_element_operation($m1, $m2, '+'));
//// Element subtraction should give us:     1    -6    -3
////                                        -4    -5     4
////                                        -1     3    -6
//matrix_print(matrix_element_operation($m1, $m2, '-'));
//// Do a scalar multiplication on the 2nd matrix:   8  18  10
////                                                14  10   0
////                                                 4   4  16
//matrix_print(matrix_scalar_operation($m2, 2, '*'));
//// Define some matrices for full matrix operations.
//// Need to be complements of each other:
//$m3 = array(
//    array(1, 3, 5),
//    array(-2, 5, 1),
//);
//$m4 = array(
//    array(1, 2),
//    array(-2, 8),
//    array(1, 1),
//);
//// Matrix multiplication gives:  0    31
////                                -11    37
//matrix_print(matrix_operation($m3, $m4, '*'));
//// Matrix addition gives:     9  20
////                            4  15
//matrix_print(matrix_operation($m3, $m4, '+'));
}
?>